using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public enum SignalFormat
    {
        Binary,
        Hex,
        Unsigned,
        Signed,
        Boolean
    }


    public class SignalSource
    {
        NodeVector nvDataOut = null;        
        int iValue;

        public bool Hidden = false;
        string sName;
        SystemInterface oSystemInterface = null;
        RapidHardware RapidHardware = null;
        public bool InterfaceSource = false;

        public Dictionary<int,NodeVector> InterfaceNodeVectors;

        private SignalFormat sfFormat=SignalFormat.Binary;

        int iWidth;

        int iSubWidth;

        public System.Collections.ArrayList AddressLines = new System.Collections.ArrayList();

        public int Width
        {
            get { return iWidth; }
        }

        public NodeVector DataOut
        {
            get { return nvDataOut; }
        }
        
        public SignalSource(string psName, int piWidth, int piValue)
        {
            sName = psName;
            iValue = piValue;
            iWidth = piWidth;
            InterfaceNodeVectors = new Dictionary<int,NodeVector>();
        }

        public void ConnectNodeVector(NodeVector pnvIn, string psValue)
        {
            string sInput = psValue.Replace('x', '0');

            if (!Conversion.IsNumeric(sInput))
                sInput = "0";
            iValue = Conversion.StringBinaryToInt(sInput);
            
            ConnectNodeVector(pnvIn, iValue);
        }


        public void ConnectNodeVector(NodeVector pnvIn, int piValue)
        {
            if (nvDataOut == null)
            {
                
                oSystemInterface = pnvIn.ParentComponent.RapidHardware.SystemInterface;
                RapidHardware = oSystemInterface.RapidHardware;
                nvDataOut = new NodeVector(pnvIn.ParentComponent.RapidHardware.Structure.ExternalInterface, sName, iWidth);
            }
            nvDataOut.Connection = pnvIn;
            iValue = piValue;
            iSubWidth = RapidHardware.Structure.ExternalInterface.DataIn.Width;

            UpdateValueSimulation();
        }

        public string ValueAsString
        {
            get
            {
                return Conversion.IntToBinaryString(iValue, iWidth);
            }
            set
            {
                string sInput = value.Replace('x','0');

                if (!Conversion.IsNumeric(sInput))
                    sInput = "0";
                ValueAsInt = Conversion.StringBinaryToInt(sInput);
            }
        }

        public int ValueAsInt
        {
            get
            {
                return iValue;
            }
            set
            {
                iValue = value;
                UpdateValue();  // backwards here pal.  interger may be too small for large number of bits
                //fix it later
            }
        }

        private void UpdateValue()
        {
            if (nvDataOut == null)
                return;

            if (RapidHardware.Settings.RunFromXilinx)
                UpdateValueXilinx();
            else
                UpdateValueSimulation();
            //RapidHardware.DisplayManager.Refresh();
        }

        private void UpdateValueXilinx()
        {
            if (AddressLines.Count == 0)
            {
                //System.Diagnostics.Debugger.Break();  // not handled
            }

            if (iValue == 2130378750)
                Console.WriteLine("Debug");

            string sValue = ValueAsString;

            //string sSent = "";

            for (int iIdx = 0; iIdx < AddressLines.Count; iIdx++)
            {
                int iAddress = (int)AddressLines[iIdx];

                string sSubValue;
                if (AddressLines.Count > 1)
                {
                    int iStart = iSubWidth * (iIdx);
                    int iEnd = iStart + iSubWidth - 1;
                    sSubValue = Conversion.SelectBinarySubset(sValue, iStart, iEnd, true);                    
                }
                else
                {
                    sSubValue = sValue;
                }
                //sSent = sSubValue + sSent;                
                RapidHardware.FPGAInterface.WriteMemory(iAddress, Conversion.StringBinaryToInt(sSubValue));
            }
            //sSent = sSent.Substring(sSent.Length - 32, 32);
        }


        private void UpdateValueSimulation()
        {
            if (InterfaceSource)
            {
                foreach (Node oNode in nvDataOut.Nodes)
                    oNode.Net.SimulationDestabilizeNet();

                nvDataOut.ForceNodeStatesAsString(Conversion.IntToBinaryString(iValue, iWidth));

                foreach (Node oNode in nvDataOut.Nodes)
                    oNode.Net.SimulationStabilizeNet();
            }
            else
            {
                if (AddressLines.Count != 1)
                    return;  // not handled

                int iAddress = (int)AddressLines[0];
                RapidHardware.Structure.ExternalInterface.SimulateWrite(iAddress, iValue);
            }
        }

        public string Name
        {
            get { return sName; }
        }

        public string Addresses
        {
            get
            {
                string sReturn = "";
                if (AddressLines.Count == 0)
                    return "";
                foreach (int iAddress in AddressLines)
                {
                    sReturn += iAddress.ToString();
                    sReturn += ",";
                }
                sReturn = sReturn.Substring(0, sReturn.Length - 1);
                return sReturn;
            }
        }

        public SignalFormat SignalFormat
        {
            get { return sfFormat; }
            set { sfFormat = value; }
        }

        public string FormattedValue
        {
            get
            {
                return Conversion.StringBinaryFormat(ValueAsString, sfFormat);
            }
            set
            {
                string sBinary = Conversion.StringFormatToBinary(value, sfFormat, iWidth);
                ValueAsString = sBinary;
            }
        }

        public string NameHDL
        {
            get
            {
                return sName.Replace('.', '_');
            }
        }

        public NodeVector GetTopLevelComponentInput()
        {
            Net oNet = nvDataOut[0].Net;
            Node oNode = oNet.SelectCriticalNode(RapidHardware.Structure.TopLevelComponent);
            return oNode.ParentNodeVector;
        }

        public bool IsIOTest
        {
            get
            {
                return (sName == "io_test_source");
            }
        }

    }
}
